import Layers from "@/lang/layers";
import {messages} from "@/lang/fast-form/messages";
import AsyncValidator from "async-validator";

/**
 * 给数据列表中的每一个元素，增加字段校验信息
 *
 * @param list {[]} 数据列表
 * @param rules {any} 数据校验规则
 */
const wrapList = (list, rules) => {
    const fields = getFields(rules);

    // 给数据元素设置基本校验情况
    list.forEach(item => {
        const validator = {};
        for (let i = 0; i < fields.length; i++) {
            validator[fields[i]] = {valid: undefined, message: ''};
        }
        item.$validators = validator;
    })
}


/**
 * 给数据列表中的每一个元素，增加字段校验信息
 *
 * @param row {any} 数据列表
 * @param rules {any} 数据校验规则
 *
 * @return {any}
 */
const wrapItem = (row, rules) => {
    if (row.$validators === undefined) {
        row.$validators = {}
    }

    const fields = getFields(rules);
    const validator = row.$validators;
    for (let i = 0; i < fields.length; i++) {
        validator[fields[i]] = {valid: undefined, message: ''};
    }
    return row;
}

/**
 * 获取需要校验的字段名列表
 *
 * @param rules {any} 数据校验规则
 * @returns {[string]}
 */
const getFields = (rules) => {
    // 获取所有需要校验的字段
    const fields = [];
    for (let key in rules) {
        if (rules.hasOwnProperty(key)) {
            fields.push(key);
        }
    }
    return fields;
}

/**
 * 清除数据列表的校验信息
 *
 * 后续业务可能是提交表单，需要清除数据校验相关的内容
 *
 * @param list {[]} 数据列表
 */
const unpack = (list) => {
    const ret = [];
    list.forEach(item => {
        const ele = {...item};
        delete ele.$validators;
        ret.push(ele);
    })
    return ret;
}

/**
 * 验证完整的数据列表
 *
 * @param list {[]} 数据列表
 * @param rules {any} 数据校验规则
 * @returns {boolean} 是否验证成功
 */
const checkList = (list, rules) => {
    const fields = getFields(rules);
    let succeed = true;
    list.forEach(item => {
        const validator = item.$validators;
        fields.forEach(key => {
            const valid = validator[key];
            const rule = rules[key];
            const value = item[key];
            if (checkValidity(value, key, rule, valid) === false) {
                succeed = false;
            }
        })
    })
    return succeed;
}


/**
 * 校验单个的数据元素
 *
 * 如果为执行过 checkList() / validItem()，调用当前函数不会触发校验
 *
 * @param row 行级数据
 * @param rules 验证规则
 * @param key 字段名
 * @returns {boolean} 是否验证成功
 */
const checkItem = (row, rules, key) => {
    if (rules[key] !== undefined) {
        const validator = row.$validators[key];
        if (validator.valid !== undefined) {
            const value = row[key];
            const rule = rules[key];
            return checkValidity(value, key, rule, validator);
        }
    }
}

/**
 * 校验单个的数据元素
 *
 * 不论如何都会触发校验
 *
 * @param row 行级数据
 * @param rules 验证规则
 * @param key 字段名
 * @returns {boolean} 是否验证成功
 */
const validItem = (row, rules, key) => {
    if (rules[key] !== undefined) {
        const validator = row.$validators[key];
        const value = row[key];
        const rule = rules[key];
        return checkValidity(value, key, rule, validator);
    }
}

/**
 * 数据是否非空
 *
 * @param val 数据
 * @param ret 数据校验结果
 */
const require = (val, ret) => {
    if (Layers.isEmpty(val)) {
        ret.valid = false;
        ret.message = '数据不允许为空';
    }
}

/**
 * 比较当前值是否有效，错误信息会直接赋值到 message
 * 使用 AsyncValidator 进行数据校验，代码参考自 element-ui
 *
 * @param rules 数据校验规则
 * @param value 需要验证的值
 * @param label 提示用的标签值
 * @param ret   数据有效性提示
 * @returns {boolean} 是否验证成功
 */
const checkValidity = (value, label, rules, ret) => {
    // 规则描述
    const descriptor = {};
    if (Layers.isNotEmpty(rules)) {
        rules.forEach(rule => {
            delete rule.trigger;
        });
    }
    descriptor[label] = rules;

    // 数据模型
    const validator = new AsyncValidator(descriptor);
    const model = {};
    model[label] = value;

    // 导入汉化之后的错误提示包
    validator.messages(messages);

    // 数据校验
    validator.validate(model, {firstFields: true}, (errors) => {
        ret.valid = !errors;
        //错误提示，这里只取第一个
        ret.message = errors ? errors[0].message : '';
    }).then(values => {
        // will return valid values, do nothing.
    }).catch(ignored => {
        // do nothing.
    })

    return ret.valid;
}

/**
 * 表内输入组件有效性验证
 *
 * 目前设计还是比较简单的，如果需要验证输入是否有效，所有的逻辑都通过外部代码实现，
 *
 * 组件自身，只负责最终的数据展示，对于表单的序列化、文本变化，都需要手动触发校验。
 */
export default {
    wrapItem, wrapList, checkItem, checkList, validItem
};